package com.anuo.app.study.studymultithread;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.*;

/**
 * @ 多线程同步
 * 有如下关键点:
 * #尽量用synchronized(object) 同步块, 而不是同步方法, 这样锁住的东西更少.
 * #用synchronized(私有对象)如: new Object()  而不是 synchronized(this)锁住整个类, 这样锁住东西太多了.
 *  关键点参考：　https://stackoverflow.com/questions/3369287/what-is-the-difference-between-synchronized-on-lockobject-and-using-this-as-the
 * @ 死锁 (简单说就是你等我我等你, 然后在那互等没法前行, 请看下面链接例子)
 * #参考: https://www.tutorialspoint.com/java/java_thread_deadlock.htm
 * #避免死锁的方式:
 * $对多个资源、数据库表、对象同时加锁时，需要保持一致的加锁顺序，否则可能会造成死锁。
 * 说明： 线程一需要对表 A、 B、 C 依次全部加锁后才可以进行更新操作，那么线程二的加锁顺序
 * 也必须是 A、 B、 C，否则可能出现死锁(来自阿里巴巴java开发手册)
 * 参考:
 * 　 http://www.cnblogs.com/XHJT/p/3897440.html
 */
public class StudySynchronized {
    public static void main(String[] args) {
        HashMap<String, String> map = new HashMap<String, String>();
        map.put("jk", "hello");

        for (Map.Entry<String, String> item : map.entrySet()) {
            String key = item.getKey();
            String value = item.getValue();
            System.out.println(key);
        }
    }

    /**
     * Synchronized 使用实例 主方法
     *  此实例中 去掉synchronized关键字或保留,可以看到不同的现象 , 比如去掉这个关键字就是并发打印数字
     */
    public static void studySynchronized() {
        WorkTask workTask = new WorkTask();
        Thread threadA = new CounterThread(workTask, "线程A");
        Thread threadB = new CounterThread(workTask, "线程B");

        threadA.start();
        threadB.start();

    }

    /**
     * Synchronized 使用实例
     */
    private static class WorkTask {

        /**
         * 此object 被 showMsg 和 showName 方法里面的两个代码块用了, 那么执行时, 一个方法中的synchronized代码块
         * 执行完了, 另一个 synchronized 代码块才能执行, 因为要等前者释放锁了, 后者才能拿到锁 进入代码块(已亲测是这样的结论)
         */
        private Object object = new Object();

        public  void showMsg(String threadName) {
            //如果下面的 synchronized 前有一行代码, 那么锁住的就是object对象, 如果没有任何代码, 此synchronized关键字就相当于
            //修饰在了方法上, 此时锁住的就是 WorkTask类实例对象.

            int a = 1;
            synchronized (object) {
                for (int i = 0; i < 10; i++) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(threadName + ": " + i);

                }

            }
        }

        public void showName() {
            int a = 2;
            synchronized (object) {
                System.out.println("hello");
            }
        }
    }

    /**
     * 线程定义, 为 studySynchronized 方法服务
     */
    private static class CounterThread extends Thread {

        protected WorkTask workTask = null;
        protected String threadName;

        public CounterThread(WorkTask workTask, String threadName) {
            this.workTask = workTask;
            this.threadName = threadName;
        }

        @Override
        public void run() {
            workTask.showMsg(threadName);
        }
    }


    /**
     *CyclicBarrier 循环屏障
     * 应用场景: 当所有线程都达到一个状态时, 再往后执行一个指定的方法
     *实例如下:
     *假设现在需要计算3个学生的平均成绩,
     *每个学生共有三门成绩
     *步骤是:先计算出每个学生的平均成绩
     *再根据每个学生的平均成绩来计算所有有同学的平均成绩
     *使用CyclicBarrier
     * @author Administrator
     *
     */
    private static class CyclicBarrier1 implements Runnable{

        //创建初始化3个线程的线程池
        private ExecutorService threadPool= Executors.newFixedThreadPool(3);
        //创建3个CyclicBarrier对象,执行完后执行当前类的run方法
        //          下面CyclicBarrier构造函数的第二个参数就是在关联,当所有线程都达到屏障的时候, 接下来该执行的方法(这里是执行当前类的run方法)
        private CyclicBarrier cb=new CyclicBarrier(3,this);
        //保存每个学生的平均成绩
        private ConcurrentHashMap<String, Integer> map=new ConcurrentHashMap<String,Integer>();

        public void count(){
            for(int i=0;i<3;i++){
                threadPool.execute(new Runnable(){

                    @Override
                    public void run() {
                        //计算每个学生的平均成绩,代码略()假设为60~100的随机数
                        int avgScore=(int)(Math.random()*40+60);
                        map.put(Thread.currentThread().getName(), avgScore);
                        System.out.println(Thread.currentThread().getName()+"同学的平均成绩为"+avgScore);
                        try {
                            //执行完运行await(),等待所有学生平均成绩都计算完毕
                            cb.await();
                        } catch (InterruptedException | BrokenBarrierException e) {
                            e.printStackTrace();
                        }
                    }

                });
            }
        }

        @Override
        public void run() {
            int result=0;
            Set<String> set = map.keySet();
            for(String s:set){
                result+=map.get(s);

            }
            System.out.println("三人平均成绩为:"+(result/3)+"分");
        }

        public static void main(String[] args) {
            CyclicBarrier1 cb=new CyclicBarrier1();
            cb.count();
        }
    }

}
